package com.harmontronics.hanma.paas.paas.domain.organization.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.harmontronics.hanma.paas.paas.consts.DbConstants;
import com.harmontronics.hanma.paas.paas.consts.NumberEnum;
import com.harmontronics.hanma.paas.paas.domain.iam.entity.HmMenu;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.HmUserHigherUpdate;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.HmUserPwd;
import com.harmontronics.hanma.paas.paas.domain.iam.service.HmMenuService;
import com.harmontronics.hanma.paas.paas.domain.iam.service.HmUserGroupManageService;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.*;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.valueobject.Constants;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.valueobject.HmOrgFields;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.valueobject.HmUserFields;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.valueobject.HmUserOrgFields;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.valueobject.I18nKeyEnum;
import com.harmontronics.hanma.paas.paas.domain.organization.entity.valueobject.IdentityTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.mapper.HmUserOrgMapper;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.persistence.HmOrgRepository;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.persistence.HmUserOrgRepository;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.persistence.HmUserRepository;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.po.HmOrgPO;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.po.HmUserOrgPO;
import com.harmontronics.hanma.paas.paas.exception.ErrorKeyException;
import com.harmontronics.hanma.paas.paas.token.AppUserTokenKit;
import com.harmontronics.hanma.paas.paas.utils.FileUtil;
import com.harmontronics.hanma.paas.paas.utils.MD5Util;
import com.harmontronics.hanma.paas.paas.utils.MyBeanUtils;
import com.harmontronics.hanma.paas.paas.utils.PasswordUtil;
import com.harmontronics.hanma.paas.paas.utils.PinYinUtils;
import com.harmontronics.hanma.paas.paas.utils.RequestUtils;
import com.harmontronics.hanma.paas.paas.utils.StringUtil;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.harmontronics.hanma.paas.paas.common.PageBase;

import com.harmontronics.hanma.paas.paas.domain.organization.repository.mapper.HmUserMapper;
import com.harmontronics.hanma.paas.paas.domain.organization.repository.po.HmUserPO;

import java.util.List;

@Service("hmUserService")
@DS("#header.App-Unique")
public class HmUserService {

    @Autowired
    private HmUserRepository hmUserRepository;

    @Autowired
    private HmUserMapper hmUserMapper;

    @Autowired
    private IHmOrgService hmOrgService;

    @Autowired
    private HmUserOrgService hmUserOrgService;

    @Autowired
    private HmUserOrgMapper hmUserOrgMapper;

    @Autowired
    private HmUserOrgRepository hmUserOrgRepository;

    @Autowired
    private HmUserOrgSerialService hmUserOrgSerialService;

    @Autowired
    private HmOrgRepository hmOrgRepository;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private HmUserGroupManageService hmUserGroupManageService;

    @Autowired
    private HmMenuService hmMenuService;

    @Autowired
    private AppUserTokenKit appUserTokenKit;



    public HmUser get(Long id) {
        HmUserPO user = hmUserRepository.getById(id);
        HmUser userListDto = BeanUtil.toBean(user, HmUser.class);
        List<OrgTree> orgTrees = hmUserOrgService.selectOrgWithUserId(id);
        List<UserOrg> orgList = MyBeanUtils.batchTransform(UserOrg.class, orgTrees);

        Long higherId = userListDto.getHigherId();
        HmUserPO userPO = hmUserRepository.getById(higherId);
        if (Optional.ofNullable(userPO).isPresent()) {
            userListDto.setHigherName(userPO.getName());
        }

        userListDto.setOrgList(orgList);
        userListDto.setPassword(null);
        userListDto.setSalt(null);
        return userListDto;
    }

    public PageBase<HmUser> getPageUserWithParams(
            int pageNo,
            int pageSize,
            Long orgId,
            String name,
            String jobNumber,
            Integer status,
            String email,
            String mobile,
            String userName) {
        // 根据orgId获取当前所有的组织节点
        if (orgId == null) {
            orgId = 1L;
        }
        UserListPage<HmUser> pageable = new UserListPage(pageNo, pageSize);
        List<Long> orgIds = hmOrgService.selectChildrenIdWithId(orgId, true);
        pageable
                .setOrgIds(orgIds)
                .setOrgId(orgId)
                .setName(name)
                .setJobNumber(jobNumber)
                .setStatus(status)
                .setMobile(mobile)
                .setEmail(email)
                .setUserName(userName);
        pageable = hmUserMapper.selectListWithParams(pageable);
        PageBase<HmUser> pageDto = new PageBase<>();
        pageDto.setData(pageable.getRecords());
        pageDto.setPageNo((int) pageable.getCurrent());
        pageDto.setPageSize((int) pageable.getSize());
        pageDto.setTotalCount(pageable.getTotal());
        pageDto.setTotalPage(pageable.getPages());

        List<HmUser> userListDtos = pageDto.getData();
        for (HmUser dto : userListDtos) {
            List<HmOrgPO> orgs = hmUserOrgMapper.selectOrgByUserId(dto.getId());
            StringJoiner stringJoiner = new StringJoiner("/");
            List<String> orgNameList = new ArrayList<>();
            orgs.forEach(e -> {
                stringJoiner.add(e.getName());
                orgNameList.add(e.getName());
            });
            dto.setOrgNames(stringJoiner.toString());
            dto.setOrgNameList(orgNameList);
            List<Long> rOrgIds =
                    orgs.stream().mapToLong(HmOrgPO::getId).boxed().collect(Collectors.toList());
            dto.setOrgIds(rOrgIds);
        }
        return pageDto;
    }

    public PageBase<HmUserPO> getPageUserWithParams(int pageNo,int pageSize, String searchKey) {
        Page<HmUserPO> pageable=new Page<>(pageNo,pageSize);
        QueryWrapper<HmUserPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(HmUserFields.DELETED, NumberEnum.ZERO.getNum());
        queryWrapper.eq(HmUserFields.STATUS, HmUserFields.STATUS_ON_DUTY);
        queryWrapper.eq(HmUserFields.IS_SUPER_ADMIN, false);
        String key = StringUtil.specialCharacterEscape(searchKey);
        queryWrapper.and(wrapper -> wrapper.like(HmUserFields.NAME, key).or().like(HmUserFields.EMAIL, key).or().like(HmUserFields.JOB_NUMBER, key).or().like(HmUserFields.TELEPHONE, key)
                .or().like(HmUserFields.SIMPLICITY, StringUtils.upperCase(key)).or().apply("array_to_string(string_to_array(full_pinyin,','), '') ilike '%" + key + "%'"));

        return new PageBase<>(hmUserMapper.selectPage(pageable, queryWrapper));
    }

    public List<HmUser> getList() {
        List<HmUserPO> users = hmUserRepository.
                list(new QueryWrapper<HmUserPO>().eq(HmUserFields.DELETED, NumberEnum.ZERO.getNum()));
        List<HmUserPO> nonSuperAdminUsers =
                users.stream().filter(e -> !e.getIsSuperAdmin()).collect(Collectors.toList());
        List<HmUserOrgPO> userOrgs =
                hmUserOrgRepository.list(
                        new QueryWrapper<HmUserOrgPO>()
                                .eq(HmUserOrgFields.DELETED, NumberEnum.ZERO.getNum()));
        List<HmUser> userDtos = new ArrayList<>();
        for (HmUserPO user : nonSuperAdminUsers) {
            HmUser userDto = new HmUser();
            userDto.setId(user.getId());
            userDto.setName(user.getName());
            for (HmUserOrgPO uo : userOrgs) {
                if (uo.getUserId().equals(user.getId())) {
                    userDto.setOrgId(uo.getOrgId());
                }
            }
            userDtos.add(userDto);
        }

        return userDtos;
    }


    public Long save(HmUser user) {
        HmUserPO jobNumUser = selectOneWithUserName(user.getUsername());

        //  当前用户已存在，在其他部门是否已存在，并且更新所属信息
        if (jobNumUser != null) {
            throw new ErrorKeyException(I18nKeyEnum.CURRENT_USER_EXIST);
        }

        Long userId = insertIntoDB(user);
        return userId;
    }

    public void update(HmUser user) {
        HmUserPO insertUser = BeanUtil.toBean(user, HmUserPO.class);
        insertUser.setFullPinyin(PinYinUtils.getHanziPinYin(user.getName(), Boolean.TRUE));
        insertUser.setSimplicity(PinYinUtils.getHanziInitials(user.getName(), Boolean.TRUE));
        insertUser.setPhoneRegion(HmUserFields.DEFAULT_PHONE_REGION);
        hmUserMapper.updateById(insertUser);
        // 处理 组织及用户关系
        hmUserOrgService.updateUserOrgById(user.getId(), user.getOrgIds());
    }

    public void remove(List<Long> idList) {
        // 更新 user表，把当前用户删除掉
        idList.forEach(
                id -> {
                    HmUserPO user = new HmUserPO();
                    user.setId(id);
                    user.setDeleted(NumberEnum.ONE.getNum());
                    hmUserMapper.updateById(user);
                });


    }



    public void moveOrg(UserMoveOrg moveOrgDto) {
        hmUserOrgService.moveOrgWithUserId(
                moveOrgDto.getIds(), moveOrgDto.getSrcId(), moveOrgDto.getOrgId());
    }

    public void quit(List<Long> idsList) {

        // 离职用户
        for (Long id : idsList) {
            HmUserPO user = hmUserMapper.selectById(id);
            user.setStatus(NumberEnum.TWO.getNum());
            user.setModifyDate(LocalDateTime.now());
            hmUserRepository.updateById(user);
        }
    }


    public boolean uniqueCheck(Long id, String field, String value) {
        if (field.equals(HmUserFields.HID_NO_FED)) {
            field = HmUserFields.HID_NO;
        }
        if (field.equals(HmUserFields.JOB_NUMBER_FED)) {
            field = HmUserFields.JOB_NUMBER;
        }
        QueryWrapper<HmUserPO> queryWrapper =
                new QueryWrapper<HmUserPO>()
                        .eq(field, value)
                        .eq(HmUserFields.DELETED, NumberEnum.ZERO.getNum());
        //离职后 hid卡可以服用
        if (field.equals(HmUserFields.HID_NO)) {
            queryWrapper.eq(HmUserFields.STATUS, HmUserFields.STATUS_ON_DUTY);
        }
        if (id != null) {
            queryWrapper.ne(HmUserFields.ID, id);
        }
        List<HmUserPO> userList = hmUserMapper.selectList(queryWrapper);
        if (!userList.isEmpty()) {
            if (field.equals(HmUserFields.EMAIL)) {
                throw new ErrorKeyException(I18nKeyEnum.EMAIL_UNIQUE_ERROR);
            }
            if (field.equals(HmUserFields.TELEPHONE)) {
                throw new ErrorKeyException(I18nKeyEnum.PHONE_UNIQUE_ERROR);
            }
            if (field.equals(HmUserFields.HID_NO)) {
                throw new ErrorKeyException(I18nKeyEnum.HID_UNIQUE_ERROR);
            }
            if (field.equals(HmUserFields.JOB_NUMBER)) {
                throw new ErrorKeyException(I18nKeyEnum.JOB_NUMBER_UNIQUE_ERROR);
            }
        }
        return true;
    }

    private HmUserPO selectOneWithUserName(String userName) {
        List<HmUserPO> userList =
                hmUserMapper.selectList(
                        new QueryWrapper<HmUserPO>()
                                .eq(HmUserFields.USER_NAME, userName)
                                .eq(HmUserFields.DELETED, NumberEnum.ZERO.getNum()));
        if (CollectionUtils.isNotEmpty(userList)) {
            return userList.get(0);
        }
        return null;
    }

    /**
     * 更新当前用户的 组织关系
     *
     * @param user
     * @param userId
     */
    private void updateUserOrg(HmUser user, Long userId) {
        // 获取已存在的组织人员关系
        List<HmUserOrgPO> userOrgList =
                hmUserOrgRepository.list(
                        new QueryWrapper<HmUserOrgPO>()
                                .eq(HmUserOrgFields.USER_ID, userId)
                                .eq(HmUserOrgFields.DELETED, NumberEnum.ZERO.getNum()));
        // 遍历插入不存在的关系
        List<Long> hasUserOrgs =
                userOrgList.stream().mapToLong(HmUserOrgPO::getOrgId).boxed()
                        .collect(Collectors.toList());
        List<Long> orgId =
                user.getOrgIds().stream().filter(e -> !hasUserOrgs.contains(e))
                        .collect(Collectors.toList());
        insertUserOrgs(orgId, userId);
    }

    private void insertUserOrgs(List<Long> orgIds, Long userId) {
        // 插入user_org表
        List<HmUserOrgPO> userOrgList = new ArrayList<>();
        for (Long orgId : orgIds) {
            HmUserOrgPO userOrg = new HmUserOrgPO();
            userOrg.setUserId(userId);
            userOrg.setOrgId(orgId);
            userOrg.setDeleted(NumberEnum.ZERO.getNum());
            userOrgList.add(userOrg);
        }
        hmUserOrgRepository.saveBatch(userOrgList);
    }


    private Long insertIntoDB(HmUser user) {
        // 插入user表
        HmUserPO insertUser = BeanUtil.toBean(user, HmUserPO.class);
        // 密码 salt设置
        Map<String, String> pwdMap = PasswordUtil.generatePassword(user.getPassword());
        insertUser.setPassword(pwdMap.get(HmUserFields.PWD));
        insertUser.setSalt(pwdMap.get(HmUserFields.SALT));

        insertUser.setFullPinyin(PinYinUtils.getHanziPinYin(user.getName(), Boolean.TRUE));
        insertUser.setSimplicity(PinYinUtils.getHanziInitials(user.getName(), Boolean.TRUE));
        insertUser.setDeleted(NumberEnum.ZERO.getNum());
        insertUser.setStatus(NumberEnum.ONE.getNum());
        insertUser.setIsSuperAdmin(false);
        insertUser.setPhoneRegion(HmUserFields.DEFAULT_PHONE_REGION);
        insertUser.setModifyDate(LocalDateTime.now());
        insertUser.setCreateDate(LocalDateTime.now());
        hmUserMapper.insert(insertUser);

        // 插入user_org表
        insertUserOrgs(user.getOrgIds(), insertUser.getId());

        Long userId = insertUser.getId();
        return userId;
    }

    public void userSort(UserSort userSort) {
        hmUserOrgSerialService
                .userSort(userSort.getId(), userSort.getSerialNum(), userSort.getOrgId());
    }


    public void changePassword(ChangePassword changePasswordInputDto) {
        if (StringUtils.isBlank(changePasswordInputDto.getOldPassword())
                || StringUtils.isBlank(changePasswordInputDto.getNewPassword())
                || StringUtils.isBlank(changePasswordInputDto.getConfirmPassword())) {
            throw new ErrorKeyException(I18nKeyEnum.REQUEST_PARAM_ERROR);
        }
        Long userId = appUserTokenKit.getId();
        HmUserPO user = hmUserMapper.selectById(userId);
        String passwordMd5 = changePasswordInputDto.getOldPassword();
        if (!PasswordUtil.isEqualMd5Password(passwordMd5, user.getSalt(), user.getPassword())) {
            throw new ErrorKeyException(I18nKeyEnum.USER_MODIFY_PASSWORD_LIMIT_4);
        }

        // 新密码和确认密码必须相等
        if (!StringUtils.equals(
                changePasswordInputDto.getConfirmPassword(),
                changePasswordInputDto.getNewPassword())) {
            throw new ErrorKeyException(I18nKeyEnum.USER_MODIFY_PASSWORD_LIMIT_1);
        }

        // 新旧密码不能重复
        if (StringUtils.equals(
                changePasswordInputDto.getNewPassword(), changePasswordInputDto.getOldPassword())) {
            throw new ErrorKeyException(I18nKeyEnum.USER_MODIFY_PASSWORD_LIMIT_2);
        }

        Map newPasswordMd5 =
                PasswordUtil.generatePasswordWidthMd5pwd(changePasswordInputDto.getNewPassword());
        user.setPassword(newPasswordMd5.get(HmUserFields.PWD).toString());
        user.setSalt(newPasswordMd5.get(HmUserFields.SALT).toString());
        user.setModifyDate(LocalDateTime.now());
        hmUserMapper.updateById(user);
        appUserTokenKit.delete();
    }

    public void resetPassword(List<Long> idList) {
        for (Long id : idList) {
            Map<String, String> pwdMap = PasswordUtil.generateInitPassword(Constants.INIT_PASSWORD);

            HmUserPO user = hmUserMapper.selectById(id);
            user.setPassword(pwdMap.get(HmUserFields.PWD));
            user.setSalt(pwdMap.get(HmUserFields.SALT));
            user.setModifyDate(LocalDateTime.now());
            hmUserMapper.updateById(user);
        }
    }

    public List<HmMenu> getAuthWithUserId() {
        Long userId = appUserTokenKit.getId();
        HmUserPO user = hmUserRepository.getById(userId);
        List<HmMenu> hmMenuList = new ArrayList<>();

        //如果当前用户是超级管理员
        if (user.getIsSuperAdmin()) {
            hmMenuList = hmMenuService.getList();
            return hmMenuList;
        } else {
            // 1.根据id 获取当前用户所在的组织架构及其父节点
            List<Long> orgIds = hmOrgService.selectOrgParentIdWithUserId(userId);

            // 2.从用户组 管理员及成员中获取所在的用户组
            List<Long> userGroupIds =
                    hmUserGroupManageService.selectGroupIdsWithOrgIdAndUserId(orgIds, userId);

            // 3.根据用户组获取角色 从角色中获取对应的菜单操作权限
            List<Long> menuIdList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(userGroupIds)) {
                menuIdList = hmMenuService.selectMenuIdsWithGroupId(userGroupIds);
            }

            List<HmMenu> menuList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(menuIdList)) {
                hmMenuList = hmMenuService.getMenuWithIds(menuIdList);

            }
        }
        List<HmMenu> allMenuList = hmMenuService.getList();

        List<HmMenu> resultList = new ArrayList<>();
        getAuthMenu(allMenuList, hmMenuList, resultList);
        resultList.addAll(hmMenuList);

        resultList = resultList.stream().distinct().collect(Collectors.toList());
        resultList = resultList.stream().sorted(Comparator.comparing(HmMenu::getSerialNum)).collect(
                Collectors.toList());

        return resultList;
    }

    private void getAuthMenu(List<HmMenu> allMenuList, List<HmMenu> hmMenuList, List<HmMenu> resultList) {
        List<HmMenu> parentMenuList = new ArrayList<>();

        for (int i = 0; i < allMenuList.size(); i++) {
            Long id = allMenuList.get(i).getId();
            for (int j = 0; j < hmMenuList.size(); j++) {
                if (hmMenuList.get(j).getParentId() != null) {
                    if (hmMenuList.get(j).getParentId().longValue() == id.longValue()) {
                        resultList.add(allMenuList.get(i));
                        parentMenuList.add(allMenuList.get(i));
                    }
                }
            }
        }
        if (parentMenuList.size() > 0) {
            getAuthMenu(allMenuList, parentMenuList, resultList);

        }
    }

    public List<HmUser> getUsersByIds(List<Long> idList) {
        List<HmUserPO> userPOList = hmUserRepository.listByIds(idList);
        return MyBeanUtils.batchTransform(HmUser.class, userPOList);
    }

    public HmUser getUserWithToken() {
        Long userId = appUserTokenKit.getId();
        HmUser hmUser = this.get(userId);
        return hmUser;
    }

    public void updatePassword(HmUserPwd hmUserPwd) {
        HmUserPO hmUserPO = BeanUtil.toBean(hmUserPwd, HmUserPO.class);
        Map<String, String> pwdMap = PasswordUtil.generatePassword(hmUserPO.getPassword());

        hmUserPO.setPassword(pwdMap.get(DbConstants.PWD));
        hmUserPO.setSalt(pwdMap.get(DbConstants.SALT));
        hmUserRepository.updateById(hmUserPO);
    }


    public List<SubmitterUser> selectSubmitterUserIn(List<Long> submitterIds) {
        if (CollectionUtil.isEmpty(submitterIds)) {
            return new ArrayList<>();
        }
        return hmUserRepository.selectSubmitterUserIn(submitterIds);
    }

    public void updateHigher(HmUserHigherUpdate hmUserHigherUpdate) {
        List<Long> userIds = hmUserHigherUpdate.getUserIds();
        Long higherId = hmUserHigherUpdate.getHigherId();
        List<HmUserPO> userList = hmUserRepository.listByIds(userIds);

        userList.stream().map(p -> {p.setHigherId(higherId);return p;}).collect(Collectors.toList());
        hmUserRepository.updateBatchById(userList);
    }


    public List<HmUser> findByManageOrg(Long orgId){
        List<HmUserPO> pos = hmUserRepository.findByManageOrg(orgId);

        return Convert.toList(HmUser.class,pos);
    }



}